// xmldomparser.js
/*

  Description: 
  the XDParser was created with the intention to parse any XML document (obviously a well formed XML and a well structured one) into JSON object that is available through for 
  further processing. The generated JSON object will reflect the same structure as the XML with some additional metadata added to it.

  Step to follow to use XDParser:
  1. include the xmldomparser.js file inthe HTML page. e.g. - <script type="text/javascript" src="xmldomparser.js"></script>
  2. use the "parsexml" and "JSONObject" to parse the xml and retrieve the JSON object from XDParser.
    a.  parsexml is the method on XDParser that will accept the path (relative path) of the XML file, parse it and will generate the JSON object.
        e.g. XDParser.parsexml("mycustomxml.xml");
    b.  JSONObject will return the generated JSON object into your variable.
        e.g. var myObj=XDParser.JSONObject;
  3. after retrieving the JSON object, the role of XDParser ends. you can use the retrieved the JSON object the way you need....
  4. XDParser will replace any occurance of double and single quotes to their entities - &quot; and &#039; respectively.

*/

/*  
	following line will add a trim method to the String object that will search for one-or-more occurances of white spaces (\s) and tabs (\t) before (^) OR after ($) the string object (this) and replace them with empty string. 'g' at the end is the modifier that represent global property for the search to replace all occurances.
*/

String.prototype.trim = function() {
    var emptystring="";
    var regex=new RegExp(/^[\t|\s]+|[\t|\s]+$/g);
    return this.replace(regex,emptystring);
}

// XDParser code starts here
var XDParser={

	/* this is the main function that will return the JSON object converted from the supplied XML document */
	parsexml: function(xmlpath) {
		var xmlObj=this.getXMLObject(xmlpath);
		this.JSONObject=this.getJSONObject(xmlObj);
	},
	
	/* this function provides the XMLDocument object by reading the XML document from the given path */
	getXMLObject: function(xmlpath) {
		var xmlDoc="";
		if (xmlpath=="")
		{
			alert("XML not found!!");
			return;
		}
		else
		{
			// check if "XMLHttpRequest" is available in the window object. if yes, then use it ense go for creating "Microsoft.XMLHTTP" object
			if (window.XMLHttpRequest)
			{
				// available in most of the recent browsers such as - IE7+, Firefox, Chrome, Opera, Safari
				xmlhttp=new XMLHttpRequest();
			}
			else
			{
				// specific for IE6, IE5 which may not provide or expose the "XMLHttpRequest" in their window
				xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
			}
			xmlhttp.open("GET",xmlpath,false);
			xmlhttp.send();
			
			xmlDoc=xmlhttp.responseXML;
		}
		return xmlDoc;
	},

	/* this function will use the XML document object and convert that to JSON object */
	getJSONObject: function(xmlObj) {
		var jsonObj;
		var jsonString='';

		// check if the xml dom has the document / root node
		if (xmlObj.nodeType==9)
		{
			jsonString='{' + this.getjsonstring_documentnode(xmlObj) + '}';
		}

		jsonObj=eval('('+jsonString+')');
		return jsonObj;
	},

	/* this function will start processing/reading xml from the document node */
	getjsonstring_documentnode: function(domNode) {
		var jsonstring='';

		//jsonstring+='"'+domNode.nodeName+'" : {';

		// get attributes for the document node if any
		//jsonstring+=this.getjsonstring_attributes(domNode);

		// start processing child nodes if any
		jsonstring+=this.getjsonstring_childnodes(domNode);

		//jsonstring+='}';

		return jsonstring;
	},

	/* this utility function will process the attributes of the given node for JSON string */
	getjsonstring_attributes: function(xnode) {
		var retValue='';
		var numberofattributes=0;
		//alert(xnode.childNodes.length);
		
		//if (xnode.hasAttributes()) -- this line works in FF / Safari / Chrome except IE so using a new condition in the following line below.
		if (xnode.attributes.length)
		{
			numberofattributes=xnode.attributes.length;
			for (var i=0; i<xnode.attributes.length; i++)
			{
				if (i>0) retValue+=',';
				retValue+=this.getjsonstring_attribute(xnode.attributes[i]);
			}
			retValue+=','+'"numberofattributes" : "'+numberofattributes.toString()+'"';
		}
		else
		{
			retValue+='"numberofattributes" : "'+numberofattributes.toString()+'"';
		}

		if (this.hasChildNodesAsElements(xnode))
		{
			if (navigator.appName.indexOf("Internet Explorer")!=-1)
			{
				retValue+=',';
			}
		}

		return retValue;
	},

	/* this utility function will take the attribute node and return the JSON string for that attribute */
	getjsonstring_attribute: function(attrnode) {
	return '"' + attrnode.name + '":"'  + attrnode.value + '"';
	},

	/* this utility function will process the child nodes of the given node for JSON string */
	getjsonstring_childnodes: function(xnode) {
		var retValue='';
		var isInsideAnArray=false;
		var ArrayStartBraceAdded=false;
		var numberofchilds=0;
		var ElementTypeNodeWasFound=false;
		if (xnode.hasChildNodes())
		{
			// if not document node then add a comma after the attribute list
			if (xnode.nodeType!=9)
			{
				//retValue+=',';
			}
			numberofchilds=xnode.childNodes.length;
			for (var i=0; i<xnode.childNodes.length; i++)
			{
				// check if the node is is of type element and count is greater than 1 then add a comma
				if ((i>0) && (xnode.childNodes[i].nodeType==1))
				{
					if (navigator.appName.indexOf("Internet Explorer")!=-1)
					{
						if (ElementTypeNodeWasFound)
						{
							retValue+=',';
						}
					}
					else
					{
						retValue+=',';
					}
				}
				
				if ((xnode.childNodes[i].nodeType==1) && (!ElementTypeNodeWasFound))
				{
					ElementTypeNodeWasFound=true;
				}
				
				if (xnode.childNodes[i].nodeType==1)
				{
					if (this.isArrayRequired(xnode,i))
					{
						if (!ArrayStartBraceAdded)
						{
							isInsideAnArray=true;
							ArrayStartBraceAdded=true;
							retValue+='"'+xnode.childNodes[i].nodeName+'": [';
						}
					}
				}
				
				retValue+=this.getjsonstring_childnode(xnode.childNodes[i],isInsideAnArray);
			}
			if (ArrayStartBraceAdded)
			{
				retValue+=']';
			}
			retValue+=','+'"numberofchilds" : "'+numberofchilds.toString()+'"';
		}
		else
		{
			retValue+='"numberofchilds" : "'+numberofchilds.toString()+'"';
		}
		
		return retValue;
	},

    /* this utility will check if the nodelist contains the next element with the same name */
    isArrayRequired: function(xnode,currentnodeindex) {
        var retValue=false;
        for (var i=currentnodeindex+1; i<xnode.childNodes.length; i++)
        {
            if (xnode.childNodes[i].nodeType==1)
            {
                if (xnode.childNodes[i].nodeName==xnode.childNodes[currentnodeindex].nodeName)
                {
                    retValue=true;
                    break;
                }
            }
        }
        return retValue;
    },

    /* this utility function will generate the JSON string for the given node */
    getjsonstring_childnode: function(xnode,isInsideAnArray) {
        var retValue='';

        if (xnode.nodeType==1) // element node
        {
            if (isInsideAnArray)
            {
                retValue+='{';
            }
            else
            {
                retValue+='"'+xnode.nodeName+'": {';
            }

            // get attributes for the node if any
            retValue+=this.getjsonstring_attributes(xnode);

            // start processing child nodes if any
            if (xnode.hasChildNodes())
            {
                retValue+=this.getjsonstring_childnodes(xnode);
            }

            retValue+='}';
        }
        else if (xnode.nodeType==3) // text node
        {
            if (xnode.nodeValue.trim()!='')
            {
                var textstring=xnode.nodeValue.replace(/\"/gi,"&quot;");
                var textstring=textstring.replace(/\'/gi,"&#039;");
                var textstring=textstring.replace(/(\r\n|\r|\n)/gi,"");
                retValue+=',"text":"'+textstring+'"';
            }
        }
        else if (xnode.nodeType==4) // cdata section
        {
            if (xnode.nodeValue.trim()!='')
            {
                //retValue+=',"cdata":"'+xnode.nodeValue.replace(/\"/gi,"\\\"")+'"';
                var cdatastring=xnode.nodeValue.replace(/\"/gi,"&quot;");
                cdatastring=cdatastring.replace(/\'/gi,"&#039;");
                cdatastring=cdatastring.replace(/(\r\n|\r|\n)/gi,"");
                retValue+=',"cdata":"'+cdatastring+'"';
            }
        }

        return retValue;
    },

    /* this utility function checks if the provided node has child elements that are of type elements */
    hasChildNodesAsElements: function(xnode){
        var retValue=false;

        if (xnode.hasChildNodes())
        {
            for (var i=0; i<xnode.childNodes.length; i++)
            {
                if (xnode.childNodes[i].nodeType==1)
                {
                    retValue=true;
                    break;
                }
            }
        }

        return retValue;
    }
}
// XDParser code ends here
